/*
 * $Id: PerforceUtil.java 23 2002-09-29 13:59:30Z bpruessmann $
 * 
 * Copyright (c) 2001 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 *    David Corbin - added FolderPosition support;  added integrate, additional reverts
 */
package net.sourceforge.p4eclipse.core.api;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sourceforge.p4eclipse.core.PerforceException;
import net.sourceforge.p4eclipse.core.PerforceRepositoryProvider;
import net.sourceforge.p4eclipse.core.Policy;
import net.sourceforge.p4eclipse.core.resources.LocalFile;
import net.sourceforge.p4eclipse.core.resources.LocalResource;
import net.sourceforge.p4eclipse.core.syncinfo.FolderSyncInfo;
import net.sourceforge.p4eclipse.core.syncinfo.ResourceSyncInfo;
import net.sourceforge.p4eclipse.core.util.ChangeSpecification;
import net.sourceforge.p4eclipse.internal.core.api.Depots;
import net.sourceforge.p4eclipse.ui.PerforceUIPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.team.core.TeamException;

/**
  * @version 	$Revision: 23 $
  * @author		<a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  * @author		<a href="mailto:dcorbin@users.sourceforge.net">David Corbin</a>
  */
public class PerforceUtil
{
	private static final String P4_CLIENT    = "client";    //$NON-NLS-1$
	private static final String P4_PRINT     = "print";     //$NON-NLS-1$
	private static final String P4_REOPEN    = "reopen";    //$NON-NLS-1$
	private static final String P4_REVIEW    = "review";    //$NON-NLS-1$
	private static final String P4_SYNC      = "sync";      //$NON-NLS-1$

	/**
	  * Private constructor for utility class.
	  */
	private PerforceUtil()
	{
	}


	/**
	  * Returns the results of a 'p4 dirs' command.
	  * @deprecated Use IClient.dirs(String) instead.
	  */	
	public static FolderSyncInfo[] getFolderSyncInfo(String dir)
	{
		FolderSyncInfo[] result = new FolderSyncInfo[0];
		try
		{
			result = PerforceUIPlugin.getClient().dirs(dir);
		}
		catch (PerforceException e)
		{
			PerforceUIPlugin.log(e.getStatus())	;
		}
		
		return result;
	}

	/**
	  * Returns the results of a 'p4 fstat' command.
	  * @deprecated Use IClient.fstat(String) instead.
	  */	
	public static ResourceSyncInfo[] getResourceSyncInfo(String file)
	{
		ResourceSyncInfo[] result = new ResourceSyncInfo[0];
		try
		{
			int hashPos = file.lastIndexOf('#');
			if (hashPos != -1)
			{
				IWhereResult whereResult = PerforceUIPlugin.getClient().where(file.substring(0, hashPos));		
				file = whereResult.getClientFilename() + file.substring(hashPos);
			}
			else
			{
				IWhereResult whereResult = PerforceUIPlugin.getClient().where(file);
				file = whereResult.getClientFilename();
			}
			
			result = PerforceUIPlugin.getClient().fstat(file);
		}
		catch (PerforceException e)
		{
			PerforceUIPlugin.log(e.getStatus())	;
		}
		
		return result;
	}
	
	public static IStatus sync(File file, String revisionSpec, final IProgressMonitor monitor)
	{
		Policy.assertNotEmpty(revisionSpec);

		long start = logStart();
		
		final MultiStatus multiStatus = new MultiStatus(PerforceUIPlugin.ID, IStatus.OK, "", null); //$NON-NLS-1$
		
		IClient client = PerforceUIPlugin.getClient();		
		synchronized (client)
		{
			monitor.subTask("");
			
			String absolutePath = file.getAbsolutePath();
			if (file.isDirectory())
				absolutePath += File.separator + "...";
			
			client.setArguments(new String[] { absolutePath + revisionSpec });
			client.run(P4_SYNC,
				new ClientUser(multiStatus)
				{
					public void outputInfo(char level, String infoString)	
					{
						super.outputInfo(level, infoString);
						
						monitor.done();
						monitor.subTask(infoString);
					}					
				});
				
			monitor.done();
		}

		logStop("PerforceUtil.sync took {0} ms.", start); //$NON-NLS-1$
		return multiStatus;		
	}
	
	public static String[] sync(String syncSpec, boolean simulate, final IProgressMonitor monitor)
	{
		Policy.assertNotEmpty(syncSpec);

		long start = logStart();		
		final MultiStatus multiStatus = new MultiStatus(PerforceUIPlugin.ID, IStatus.OK, "", null); //$NON-NLS-1$
		
		final List files = new ArrayList();
				
		IClient client = PerforceUIPlugin.getClient();		
		synchronized (client)
		{
			monitor.subTask("");
			
			if (simulate)
			client.setArguments(new String[] { "-n", syncSpec });			
			else	
				client.setArguments(new String[] { syncSpec });
				
			client.run(P4_SYNC,
				new ClientUser(multiStatus)
				{
					public void outputInfo(char level, String infoString)	
					{
						if (level == '0')
						{
							int pos = infoString.indexOf('#');
							String filename = infoString.substring(0, pos);
							
							files.add(filename);
						}
						
						monitor.done();
						monitor.subTask(infoString);
					}					
				});
				
			monitor.done();
		}				

		logStop("PerforceUtil.sync took {0} ms.", start); //$NON-NLS-1$
		
		String[] filenames = (String[])files.toArray(new String[0]);
		try
		{
			IWhereResult[] whereResults = 
				PerforceUIPlugin.getClient().where(filenames);
				
			Policy.assertTrue(whereResults.length == filenames.length);
				
			for (int i = 0, length = whereResults.length; i < length; i++)
				filenames[i] = whereResults[i].getLocalFilename();
		}
		catch (PerforceException e)
		{
			PerforceUIPlugin.getPlugin().log(e.getStatus());
		}
				
		return filenames;		
	}	
		
	
	public static String[] depots()
		throws TeamException
	{
		long start = logStart();
		
		IClient client = PerforceUIPlugin.getClient();
		Depots clientUser = Depots.newInstance();
		synchronized (client)
		{
			client.setArguments(new String[0]);
			client.run(clientUser.getCommand(), clientUser);
		}

		logStop("PerforceUtil.depots took {0} ms.", start); //$NON-NLS-1$
		
		if (clientUser.getStatus().getSeverity() == IStatus.ERROR)
			throw new TeamException(clientUser.getStatus());

		return clientUser.getResult();
	}


	public static InputStream print(String depotFileName, String revision)
		throws TeamException
	{
		Policy.assertTrue(depotFileName != null && depotFileName.length() > 0);
		Policy.assertTrue(revision != null && revision.length() > 0);

		long start = logStart();
				
		final MultiStatus multiStatus = new MultiStatus(PerforceUIPlugin.ID, IStatus.OK, "", null); //$NON-NLS-1$		
		InputStream result = null;
		
		IWhereResult whereResult = PerforceUIPlugin.getClient().where(depotFileName);
		depotFileName = whereResult.getClientFilename();
	
		IClient client = PerforceUIPlugin.getClient();
		synchronized (client)
		{
			try
			{
				// XXX TODO: replace temp file with ClientApi::File in native lib
				File tempFile = File.createTempFile("p4-", ".tmp");
				tempFile.deleteOnExit();
				
				String[] arguments = new String[4];
				arguments[0] = "-o";
				arguments[1] = tempFile.getAbsolutePath();
				arguments[2] = "-q";
				arguments[3] = depotFileName + revision;
				
				client.setArguments(arguments);
				client.run(P4_PRINT, new ClientUser(multiStatus));
				
				result = new FileInputStream(tempFile);
			}
			catch (IOException e)
			{
				multiStatus.merge(new Status(IStatus.ERROR, PerforceUIPlugin.ID, IStatus.ERROR, "PerforceUtil.print failed.", e));
			}
		}

		logStop("PerforceUtil.print took {0} ms.", start); //$NON-NLS-1$
		
		if (multiStatus.getSeverity() == IStatus.ERROR)
			throw new TeamException(multiStatus);
			
		return result;
	}

	/**
	  * This method performs a 'p4 edit' command with the specified resources.
	  * 
	  * XXX TODO: Handling of progress ...
	  */	
	public static IStatus reopen(LocalFile[] files, String changelist)
	{
		Policy.assertNotNull(files);

		long start = logStart();
		
		final MultiStatus multiStatus = new MultiStatus(PerforceUIPlugin.ID, IStatus.OK, "", null); //$NON-NLS-1$
		if (files.length == 0)
			return multiStatus;

		
		IClient client = PerforceUIPlugin.getClient();
		synchronized (client)
		{
			String[] arguments = new String[2 + files.length];
			arguments[0] = "-c"; //$NON-NLS-1$
			arguments[1] = changelist;			
			
			System.arraycopy(getFileNames(files), 0, arguments, 2, files.length);

			client.setArguments(arguments);
			client.run(P4_REOPEN, new ClientUser(multiStatus));
		}
		
		logStop("PerforceUtil.reopen took {0} ms.", start); //$NON-NLS-1$
		return multiStatus;
	}


	static ClientForm getClient()
	{
		long start = logStart();
		
		final ClientForm[] result = new ClientForm[] { new ClientForm() };
		
		IClient client = PerforceUIPlugin.getClient();
		final MultiStatus multiStatus = new MultiStatus(PerforceUIPlugin.ID, IStatus.OK, "", null); //$NON-NLS-1$
		synchronized (client)
		{
			client.setArguments(new String[] { "-o" }); //$NON-NLS-1$
			IClientUser clientUser = new ClientUser(multiStatus)
				{
					public void outputStat(Map strDict)
					{
						try 
						{
							result[0].parse((String)strDict.get("data")); //$NON-NLS-1$
						} 
						catch(IOException e) 
						{
							multiStatus.merge(new Status(IStatus.ERROR, PerforceUIPlugin.ID, IStatus.ERROR, "Error parsing clientspec.", e));
						}
					}
				};				
			client.run(P4_CLIENT,clientUser);
		}		
		
		logStop("PerforceUtil.getClient took {0} ms.", start); //$NON-NLS-1$
		if (multiStatus.getSeverity() == IStatus.ERROR)
		{
			PerforceUIPlugin.getPlugin().log(multiStatus);
			return null;
		}
		else
			return result[0];
	}

	private static String[] getFileNames(LocalFile[] files)
	{
		List argumentList = new ArrayList();
		for (int i = 0, length = files.length; i < length; i++)
			argumentList.add(files[i].getAbsolutePath());
		
		return (String[])argumentList.toArray(new String[argumentList.size()]);		
	}
	

	private static long logStart()
	{
		return System.currentTimeMillis();
	}
	
	private static void logStop(String message, long start)
	{
		long stop = System.currentTimeMillis();
		if (PerforceUIPlugin.getPlugin().isDebugging())
			System.out.println(MessageFormat.format(message, new Object[] { new Long(stop - start) }));
	}
}
